TITLE 01/04/84 KEYBOARD BIOS
.XLIST
INCLUDE DSEG.SRC
INCLUDE POSTEQU.SRC
.LIST

PUBLIC	KEYBOARD_IO_1
PUBLIC	KB_INT_1
PUBLIC	K16

INCLUDE SEGMENT.SRC

EXTRN	DDS:NEAR
EXTRN	START_1:NEAR
EXTRN	K6:BYTE
EXTRN	K6L:ABS
EXTRN	K7:BYTE
EXTRN	K8:BYTE
EXTRN	K9:BYTE
EXTRN	K10:BYTE
EXTRN	K11:BYTE
EXTRN	K12:BYTE
EXTRN	K13:BYTE
EXTRN	K14:BYTE
EXTRN	K15:BYTE

;---- INT 16 ---------------------------------------------------------
; KEYBOARD I/O
;	THESE ROUTINES PROVIDE KEYBOARD SUPPORT
; INPUT
;	(AH)=0	READ THE NEXT ASCII CHARACTER STRUCK FROM THE KEYBOARD
;		RETURN THE RESULT IN (AL), SCAN CODE IN (AH)
;	(AH)=1	SET THE Z FLAG TO INDICATE IF AN ASCII CHARACTER IS AVAILABLE
;		TO BE READ.
;		(ZF)=1 -- NO CODE AVAILABLE
;		(ZF)=0 -- CODE IS AVAILABLE
;		IF ZF = 0, THE NEXT CHARACTER IN THE BUFFER TO BE READ IS
;		IN AX, AND THE ENTRY REMAINS IN THE BUFFER
;	(AH)=2	RETURN THE CURRENT SHIFT STATUS IN AL REGISTER
;		THE BIT SETTINGS FOR THIS CODE ARE INDICATED IN THE
;		EQUATES FOR KB_FLAG
; OUTPUT
;	AS NOTED ABOVE, ONLY AX AND FLAGS CHANGED
;	ALL REGISTERS RETAINED
;----------------------------------------
	ASSUME	CS:CODE,DS:DATA

KEYBOARD_IO_1	PROC	FAR	 ;>>> ENTRY POINT FOR ORG 0E82EH
	STI			; INTERRUPTS BACK ON
	PUSH	DS		; SAVE CURRENT DS
	PUSH	BX		; SAVE BX TEMPORARILY
	CALL	DDS		; ESTABLISH POINTER TO DATA REGION
	OR	AH,AH		; AH=0
	JZ	K1B		; ASCII_READ
	DEC	AH		; AH=1
	JZ	K2		; ASCII_STATUS
	DEC	AH		; AH=2
	JZ	K3		; SHIFT_STATUS
	POP	BX		; RECOVER REGISTER
	POP	DS
	IRET			; INVALID COMMAND

;------ READ THE KEY TO FIGURE OUT WHAT TO DO

K1B:	MOV	BX,BUFFER_HEAD	; GET POINTER TO HEAD OF BUFFER
	CMP	BX,BUFFER_TAIL	; TEST END OF BUFFER
	JNE	K1C		; IF ANYTHING IN BUFFER DONT DO INTERRUPT
;
	MOV	AX,09002H	; MOVE IN WAIT CODE & TYPE
	INT	15H		; PERFORM OTHER FUNCTION
K1:				; ASCII READ
	STI			; INTERRUPTS BACK ON DURING LOOP
	NOP			; ALLOW AN INTERRUPT TO OCCUR
K1C:	CLI			; INTERRUPTS BACK OFF
	MOV	BX,BUFFER_HEAD	; GET POINTER TO HEAD OF BUFFER
	CMP	BX,BUFFER_TAIL	; TEST END OF BUFFER
	PUSH	BX		; SAVE ADDRESS
	PUSHF			; SAVE FLAG
	CALL	MAKE_LED	; GO GET MODE INDICATOR DATA BYTE
	MOV	BL,KB_FLAG_2	; GET PREVIOUS BITS
	XOR	BL,AL		; SEE IF ANY DIFFERENT
	AND	BL,07H		; ISOLATE INDICATOR BITS
	JZ	K1A		; IF NO CHANGE BYPASS UPDATE
;
	CALL	SND_LED1	; GO TURN ON MODE INDICATORS
	CLI			; DISABLE INTERRUPTS
K1A:	POPF			; RESTORE FLAGS
	POP	BX		; RESTORE ADDRESS
	JZ	K1		; LOOP UNTIL SOMETHING IN BUFFER
;
	MOV	AX,[BX] 	; GET SCAN CODE AND ASCII CODE
	CALL	K4		; MOVE POINTER TO NEXT POSITION
	MOV	BUFFER_HEAD,BX	; STORE VALUE IN VARIABLE

	POP	BX		; RECOVER REGISTER
	POP	DS		; RECOVER SEGMENT
	IRET			; RETURN TO CALLER

;------ ASCII STATUS

K2:
	CLI			; INTERRUPTS OFF
	MOV	BX,BUFFER_HEAD	; GET HEAD POINTER
	CMP	BX,BUFFER_TAIL	; IF EQUAL (Z=1) THEN NOTHING THERE
	MOV	AX,[BX]
	PUSHF			; SAVE FLAGS
;
	PUSH	AX		; SAVE CODE
	CALL	MAKE_LED	; GO GET MODE INDICATOR DATA BYTE
	MOV	BL,KB_FLAG_2	; GET PREVIOUS BITS
	XOR	BL,AL		; SEE IF ANY DIFFERENT
	AND	BL,07H		; ISOLATE INDICATOR BITS
	JZ	SK2		; IF NO CHANGE BYPASS UPDATE
;
	CALL	SND_LED1	; GO TURN ON MODE INDICATORS
SK2:	POP	AX		; RESTORE CODE
	POPF			; RESTORE FLAGS
	STI			; INTERRUPTS BACK ON
	POP	BX		; RECOVER REGISTER
	POP	DS		; RECOVER SEGMENT
	RET	2		; THROW AWAY FLAGS

;------ SHIFT STATUS

K3:
	MOV	AL,KB_FLAG	; GET THE SHIFT STATUS FLAGS
	POP	BX		; RECOVER REGISTER
	POP	DS		; RECOVER REGISTERS
	IRET			; RETURN TO CALLER
KEYBOARD_IO_1	ENDP

;------ INCREMENT A BUFFER POINTER

K4	PROC	NEAR
	INC	BX		; MOVE TO NEXT WORD IN LIST
	INC	BX
	CMP	BX,BUFFER_END	; AT END OF BUFFER?
	JNE	K5		; NO, CONTINUE
	MOV	BX,BUFFER_START ; YES, RESET TO BUFFER BEGINNING
K5:
	RET
K4	ENDP

;------ KEYBOARD INTERRUPT ROUTINE

KB_INT_1 PROC	FAR
	STI			; ENABLE INTERRUPTS
	PUSH	BP
	PUSH	AX
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	SI
	PUSH	DI
	PUSH	DS
	PUSH	ES
	CLD			; FORWARD DIRECTION
	CALL	DDS		; SET UP ADDRESSING
	MOV	AL,DIS_KBD	; DISABLE THE KEYBOARD
	CALL	SHIP_IT 	; EXECUTE DISABLE

;------- WAIT FOR COMMAND TO ACCEPTED
	CLI			; DISABLE INTERRUPTS
	SUB	CX,CX		;
KB_INT_01:
	IN	AL,STATUS_PORT	;
	TEST	AL,INPT_BUF_FULL
	LOOPNZ	KB_INT_01	; WAIT FOR COMMAND TO BE ACCEPTED

	IN	AL,PORT_A	; READ IN THE CHARACTER
	STI			; ENABLE INTERRUPTS AGAIN

;--------CHECK FOR A RESEND COMMAND TO KEYBOARD

	CMP	AL,KB_RESEND	; IS THE INPUT A RESEND
	JE	KB_INT_4	; GO IF RESEND

;------- CHECK FOR RESPONSE TO A COMMAND TO KEYBOARD

	CMP	AL,KB_ACK	; IS THE INPUT AN ACKNOWLEDGE
	JNZ	KB_INT_2	; GO IF NOT

;------- A COMMAND TO THE KEYBOARD WAS ISSUED

	CLI			; DISABLE INTERRUPTS
	OR	KB_FLAG_2,KB_FA ; INDICATE ACK RECEIVED
	JMP	K26		; RETURN IF NOT (THIS ACK RETURNED FOR DATA)

;-------- RESEND THE LAST BYTE

KB_INT_4:
	CLI			; DISABLE INTERRUPTS
	OR	KB_FLAG_2,KB_FE ; INDICATE RESEND RECEIVED
	JMP	K26		; RETURN IF NOT (THIS ACK RETURNED FOR DATA)

KB_INT_2:

;--------UPDATE MODE INDICATORS IF CHANGE IN STATE

	PUSH	AX		; SAVE DATA IN
	CALL	MAKE_LED	; GO GET MODE INDICATOR DATA BYTE
	MOV	BL,KB_FLAG_2	; GET PREVIOUS BITS
	XOR	BL,AL		; SEE IF ANY DIFFERENT
	AND	BL,07H		; ISOLATE INDICATOR BITS
	JZ	UP0		; IF NO CHANGE BYPASS UPDATE
;
	CALL	SND_LED 	; GO TURN ON MODE INDICATORS
UP0:	POP	AX		; RESTORE DATA IN
	MOV	AH,AL		; SAVE SCAN CODE IN AH ALSO

;------ TEST FOR OVERRUN SCAN CODE FROM KEYBOARD

	CMP	AL,KB_OVER_RUN	; IS THIS AN OVERRUN CHAR
	JNZ	K16		; NO, TEST FOR SHIFT KEY
	JMP	K62		; BUFFER_FULL_BEEP

;------ TEST FOR SHIFT KEYS

K16:				; TEST_SHIFT
	AND	AL,07FH 	; TURN OFF THE BREAK BIT
	PUSH	CS
	POP	ES		; ESTABLISH ADDRESS OF SHIFT TABLE

;------- TEST FOR SYSTEM KEY

	CMP	AL,SYS_KEY	; IS IT THE SYSTEM KEY?
	JNZ	K16A		; CONTINUE IF NOT
;
	TEST	AH,080H 	; CHECK IF THIS A BREAK CODE
	JNZ	K16C		; DONT TOUCH SYSTEM INDICATOR IF TRUE
;
	TEST	KB_FLAG_1,SYS_SHIFT ; SEE IF IN SYSTEM KEY HELD DOWN
	JNZ	K16B		; IF YES, DONT PROCESS SYSTEM INDICATOR
;
	OR	KB_FLAG_1,SYS_SHIFT ; INDICATE SYSTEM KEY DEPRESSED
	MOV	AL,EOI			; END OF INTERRUPT COMMAND
	OUT	020H,AL 		; SEND COMMAND TO INTERRUPT CONTROL PORT
					; INTERRUPT-RETURN-NO-EOI
	MOV	AL,ENA_KBD		; INSURE KEYBOARD IS ENABLED
	CALL	SHIP_IT 		; EXECUTE ENABLE
	MOV	AX,08500H	; FUNCTION VALUE FOR MAKE OF SYSTEM KEY
	STI			; MAKE SURE INTERRUPTS ENABLED
	INT	15H		; USER INTERRUPT
	JMP	K27A		; END PROCESSING
K16B:	JMP	K26		; IGNORE SYSTEM KEY
;
K16C:	AND	KB_FLAG_1,NOT SYS_SHIFT ; TURN OFF SHIFT KEY HELD DOWN
	MOV	AL,EOI			; END OF INTERRUPT COMMAND
	OUT	020H,AL 		; SEND COMMAND TO INTERRUPT CONTROL PORT
					; INTERRUPT-RETURN-NO-EOI
	MOV	AL,ENA_KBD		; INSURE KEYBOARD IS ENABLED
	CALL	SHIP_IT 		; EXECUTE ENABLE
	MOV	AX,08501H	; FUNCTION VALUE FOR BREAK OF SYSTEM KEY
	STI			; MAKE SURE INTERRUPTS ENABLED
	INT	15H		; USER INTERRUPT
	JMP	K27A		; IGNORE SYSTEM KEY
;
K16A:	MOV	DI,OFFSET K6	; SHIFT KEY TABLE
	MOV	CX,OFFSET K6L	; LENGTH
	REPNE	SCASB		; LOOK THROUGH THE TABLE FOR A MATCH
	MOV	AL,AH		; RECOVER SCAN CODE
	JE	K17		; JUMP IF MATCH FOUND
	JMP	K25		; IF NO MATCH, THEN SHIFT NOT FOUND

;------ SHIFT KEY FOUND

K17:	SUB	DI,OFFSET K6+1	; ADJUST PTR TO SCAN CODE MATCH
	MOV	AH,CS:K7[DI]	; GET MASK INTO AH
	TEST	AL,80H		; TEST FOR BREAK KEY
	JZ	K17C		; BREAK_SHIFT_FOUND
	JMP	SHORT K23	; CONTINUE

;-------- DETERMINE SET OR TOGGLE

K17C:	CMP	AH,SCROLL_SHIFT
	JAE	K18			; IF SCROLL SHIFT OR ABOVE, TOGGLE KEY

;------ PLAIN SHIFT KEY, SET SHIFT ON

	OR	KB_FLAG,AH		; TURN ON SHIFT BIT
	JMP	K26			; INTERRUPT_RETURN

;------ TOGGLED SHIFT KEY, TEST FOR 1ST MAKE OR NOT

K18:					; SHIFT-TOGGLE
	TEST	KB_FLAG, CTL_SHIFT	; CHECK CTL SHIFT STATE
	JZ	K18A			; JUMP IF NOT CTL STATE

	JMP	K25
K18A:	CMP	AL,INS_KEY		; CHECK FOR INSERT KEY
	JNZ	K22			; JUMP IF NOT INSERT KEY
	TEST	KB_FLAG, ALT_SHIFT	; CHECK FOR ALTERNATE SHIFT
	JZ	K19			; JUMP IF NOT ALTERNATE SHIFT
	JMP	K25		; JUMP IF ALTERNATE SHIFT
K19:	TEST	KB_FLAG, NUM_STATE	; CHECK FOR BASE STATE
	JNZ	K21			; JUMP IF NUM LOCK IS ON
	TEST	KB_FLAG, LEFT_SHIFT+RIGHT_SHIFT 	;
	JZ	K22			; JUMP IF BASE STATE

K20:					; NUMERIC ZERO, NOT INSERT KEY
	MOV	AX, 5230H		; PUT OUT AN ASCII ZERO
	JMP	K57			; BUFFER_FILL
K21:					; MIGHT BE NUMERIC
	TEST	KB_FLAG, LEFT_SHIFT+RIGHT_SHIFT 	;
	JZ	K20			; JUMP NUMERIC, NOT INSERT

K22:					; SHIFT TOGGLE KEY HIT; PROCESS IT
	TEST	AH,KB_FLAG_1		; IS KEY ALREADY DEPRESSED
	JZ	K22A0			; GO IF NOT
	JMP	SHORT K26		; JUMP IF KEY ALREADY DEPRESSED
K22A0:	OR	KB_FLAG_1,AH		; INDICATE THAT THE KEY IS DEPRESSED
	XOR	KB_FLAG,AH		; TOGGLE THE SHIFT STATE

;------- TOGGLE LED IF CAPS OR NUM KEY DEPRESSED

	TEST	AH,CAPS_SHIFT+NUM_SHIFT+SCROLL_SHIFT ; SHIFT TOGGLE?
	JZ	K22B			; GO IF NOT
	PUSH	AX			; SAVE SCAN CODE AND SHIFT MASK
	CALL	SND_LED 		; GO TURN MODE INDICATORS ON
	POP	AX			; RESTORE SCAN CODE

K22B:	CMP	AL,INS_KEY		; TEST FOR 1ST MAKE OF INSERT KEY
	JNE	K26			; JUMP IF NOT INSERT KEY
	MOV	AX,INS_KEY*256		; SET SCAN CODE INTO AH, 0 INTO AL
	JMP	K57			; PUT INTO OUTPUT BUFFER

;------ BREAK SHIFT FOUND

K23:					; BREAK-SHIFT-FOUND
	CMP	AH,SCROLL_SHIFT 	; IS THIS A TOGGLE KEY
	JAE	K24			; YES, HANDLE BREAK TOGGLE
	NOT	AH			; INVERT MASK
	AND	KB_FLAG,AH		; TURN OFF SHIFT BIT
	CMP	AL,ALT_KEY+80H		; IS THIS ALTERNATE SHIFT RELEASE
	JNE	K26			; INTERRUPT_RETURN

;------ ALTERNATE SHIFT KEY RELEASED, GET THE VALUE INTO BUFFER

	MOV	AL,ALT_INPUT
	MOV	AH,0			; SCAN CODE OF 0
	MOV	ALT_INPUT,AH		; ZERO OUT THE FIELD
	CMP	AL,0			; WAS THE INPUT=0
	JE	K26			; INTERRUPT_RETURN
	JMP	K58			; IT WASN'T, SO PUT IN BUFFER

K24:					; BREAK-TOGGLE
	NOT	AH			; INVERT MASK
	AND	KB_FLAG_1,AH		; INDICATE NO LONGER DEPRESSED
	JMP	SHORT K26		; INTERRUPT_RETURN

;------ TEST FOR HOLD STATE

K25:					; NO-SHIFT-FOUND
	CMP	AL,80H			; TEST FOR BREAK KEY
	JAE	K26			; NOTHING FOR BREAK CHARS FROM HERE ON
	TEST	KB_FLAG_1,HOLD_STATE	; ARE WE IN HOLD STATE
	JZ	K28			; BRANCH AROUND TEST IF NOT
	CMP	AL,NUM_KEY
	JE	K26			; CAN'T END HOLD ON NUM_LOCK
	AND	KB_FLAG_1,NOT HOLD_STATE	; TURN OFF THE HOLD STATE BIT

K26:				; INTERRUPT-RETURN
	CLI			; TURN OFF INTERRUPTS
	MOV	AL,EOI		; END OF INTERRUPT COMMAND
	OUT	020H,AL 	; SEND COMMAND TO INTERRUPT CONTROL PORT
K27:				; INTERRUPT-RETURN-NO-EOI
	MOV	AL,ENA_KBD	; INSURE KEYBOARD IS ENABLED
	CALL	SHIP_IT 	; EXECUTE ENABLE

K27A:	CLI			; DISABLE INTERRUPTS
	POP	ES		; RESTORE REGISTERS
	POP	DS		; "
	POP	DI		; "
	POP	SI		; "
	POP	DX		; "
	POP	CX		; "
	POP	BX		; "
	POP	AX		; "
	POP	BP		; "
	IRET			; RETURN, INTERRUPTS ON WITH FLAG CHANGE

;------ NOT IN	HOLD STATE

K28:				; NO-HOLD-STATE
	TEST	KB_FLAG,ALT_SHIFT ; ARE WE IN ALTERNATE SHIFT
	JNZ	K29		; JUMP IF ALTERNATE SHIFT
	JMP	K38		; JUMP IF NOT ALTERNATE

;------ TEST FOR RESET KEY SEQUENCE (CTL ALT DEL)

K29:				; TEST-RESET
	TEST	KB_FLAG,CTL_SHIFT ; ARE WE IN CONTROL SHIFT ALSO
	JZ	K31		; NO RESET
	CMP	AL,DEL_KEY	; SHIFT STATE IS THERE, TEST KEY
	JNE	K31		; NO-RESET

;------ CTL-ALT-DEL HAS BEEN FOUND, DO I/O CLEANUP

	MOV	RESET_FLAG, 1234H ; SET FLAG FOR RESET FUNCTION
	JMP	START_1 	; JUMP TO POWER ON DIAGNOSTICS

;------ ALT-INPUT-TABLE
K30	LABEL	BYTE
	DB	82,79,80,81,75,76,77
	DB	71,72,73	; 10 NUMBERS ON KEYPAD
;------ SUPER-SHIFT-TABLE
	DB	16,17,18,19,20,21,22,23 ; A-Z TYPEWRITER CHARS
	DB	24,25,30,31,32,33,34,35
	DB	36,37,38,44,45,46,47,48
	DB	49,50

;------ IN ALTERNATE SHIFT, RESET NOT FOUND

K31:					; NO-RESET
	CMP	AL,57			; TEST FOR SPACE KEY
	JNE	K32			; NOT THERE
	MOV	AL,' '                  ; SET SPACE CHAR
	JMP	K57			; BUFFER_FILL

;------ LOOK FOR KEY PAD ENTRY

K32:					; ALT-KEY-PAD
	MOV	DI,OFFSET K30		; ALT-INPUT-TABLE
	MOV	CX,10			; LOOK FOR ENTRY USING KEYPAD
	REPNE	SCASB			; LOOK FOR MATCH
	JNE	K33			; NO_ALT_KEYPAD
	SUB	DI,OFFSET K30+1 	; DI NOW HAS ENTRY VALUE
	MOV	AL,ALT_INPUT		; GET THE CURRENT BYTE
	MOV	AH,10			; MULTIPLY BY 10
	MUL	AH
	ADD	AX,DI			; ADD IN THE LATEST ENTRY
	MOV	ALT_INPUT,AL		; STORE IT AWAY
	JMP	K26			; THROW AWAY THAT KEYSTROKE

;------ LOOK FOR SUPERSHIFT ENTRY

K33:					; NO-ALT-KEYPAD
	MOV	ALT_INPUT,0		; ZERO ANY PREVIOUS ENTRY INTO INPUT
	MOV	CX,26			; DI,ES ALREADY POINTING
	REPNE	SCASB			; LOOK FOR MATCH IN ALPHABET
	JNE	K34			; NOT FOUND, FUNCTION KEY OR OTHER
	MOV	AL,0			; ASCII CODE OF ZERO
	JMP	K57			; PUT IT IN THE BUFFER

;------ LOOK FOR TOP ROW OF ALTERNATE SHIFT

K34:					; ALT-TOP-ROW
	CMP	AL,2			; KEY WITH '1' ON IT
	JB	K35			;  NOT ONE OF INTERESTING KEYS
	CMP	AL,14			;  IS IT IN THE REGION
	JAE	K35			;  ALT-FUNCTION
	ADD	AH,118			;  CONVERT PSEUDO SCAN CODE TO RANGE
	MOV	AL,0			;  INDICATE AS SUCH
	JMP	K57			;  BUFFER_FILL

;------ TRANSLATE ALTERNATE SHIFT PSEUDO SCAN CODES

K35:					; ALT-FUNCTION
	CMP	AL,59			; TEST FOR IN TABLE
	JAE	K37			; ALT-CONTINUE
K36:					; CLOSE-RETURN
	JMP	K26			; IGNORE THE KEY
K37:					; ALT-CONTINUE
	CMP	AL,71			; IN KEYPAD REGION
	JAE	K36			; IF SO, IGNORE
	MOV	BX,OFFSET K13		; ALT SHIFT PSEUDO SCAN TABLE
	JMP	K63			; TRANSLATE THAT

;------ NOT IN ALTERNATE SHIFT

K38:					; NOT-ALT-SHIFT
	TEST	KB_FLAG,CTL_SHIFT	; ARE WE IN CONTROL SHIFT
	JZ	K44			; NOT-CTL-SHIFT

;------ CONTROL SHIFT, TEST SPECIAL CHARACTERS
;------ TEST FOR BREAK AND PAUSE KEYS
	CMP	AL,SCROLL_KEY		; TEST FOR BREAK
	JNE	K39			; NO-BREAK
	MOV	BX,BUFFER_START 	; RESET BUFFER TO EMPTY
	MOV	BUFFER_HEAD,BX
	MOV	BUFFER_TAIL,BX
	MOV	BIOS_BREAK,80H		; TURN ON BIOS_BREAK BIT

;-------- ENABLE KEYBOARD

	MOV	AL,ENA_KBD		; ENABLE KEYBOARD
	CALL	SHIP_IT 		; EXECUTE ENABLE
	INT	1BH			; BREAK INTERRUPT VECTOR
	SUB	AX,AX			; PUT OUT DUMMY CHARACTER
	JMP	K57			; BUFFER_FILL

K39:					; NO_BREAK
	CMP	AL,NUM_KEY		; LOOK FOR PAUSE KEY
	JNE	K41			; NO-PAUSE
	OR	KB_FLAG_1,HOLD_STATE	; TURN ON THE HOLD FLAG

;-----	ENABLE KEYBOARD

	MOV	AL,ENA_KBD		; ENABLE KEYBOARD
	CALL	SHIP_IT 		; EXECUTE ENABLE
	MOV	AL,EOI			; END OF INTERRUPT TO CONTROL PORT
	OUT	020H,AL 		; ALLOW FURTHER KEYSTROKE INTS

;------ DURING PAUSE INTERVAL, TURN CRT BACK ON

	CMP	CRT_MODE,7		; IS THIS THE BLACK AND WHITE CARD
	JE	K40			; YES, NOTHING TO DO
	MOV	DX,03D8H		; PORT FOR COLOR CARD
	MOV	AL,CRT_MODE_SET 	; GET THE VALUE OF THE CURRENT MODE
	OUT	DX,AL			; SET THE CRT MODE, SO THAT CRT IS ON
K40:					; PAUSE-LOOP

K40A:
	TEST	KB_FLAG_1,HOLD_STATE
	JNZ	K40			; LOOP UNTIL FLAG TURNED OFF
	JMP	K27A			; INTERRUPT_RETURN_NO_EOI
K41:					; NO-PAUSE

;------ TEST SPECIAL CASE KEY 55

	CMP	AL,55
	JNE	K42			; NOT-KEY-55
	MOV	AX,114*256		; START/STOP PRINTING SWITCH
	JMP	K57			; BUFFER_FILL

;------ SET UP TO TRANSLATE CONTROL SHIFT

K42:					; NOT-KEY-55
	MOV	BX,OFFSET K8		; SET UP TO TRANSLATE CTL
	CMP	AL,59			; IS IT IN TABLE
	JB	K56			; YES, GO TRANSLATE CHAR
					; CTL-TABLE-TRANSLATE
	MOV	BX,OFFSET K9		; CTL TABLE SCAN
	JMP	K63			; TRANSLATE_SCAN

;------ NOT IN CONTROL SHIFT

K44:					; NOT-CTL-SHIFT

	CMP	AL,71			; TEST FOR KEYPAD REGION
	JAE	K48			; HANDLE KEYPAD REGION
	TEST	KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT
	JZ	K54			; TEST FOR SHIFT STATE

;------ UPPER CASE, HANDLE SPECIAL CASES

	CMP	AL,15			; BACK TAB KEY
	JNE	K45			; NOT-BACK-TAB
	MOV	AX,15*256		; SET PSEUDO SCAN CODE
	JMP	SHORT K57		; BUFFER_FILL

K45:					; NOT-BACK-TAB
	CMP	AL,55			; PRINT SCREEN KEY
	JNE	K46			; NOT-PRINT-SCREEN

;------ ISSUE INTERRUPT TO INDICATE PRINT SCREEN FUNCTION

	MOV	AL,ENA_KBD		; INSURE KEYBOARD IS ENABLED
	CALL	SHIP_IT 		; EXECUTE ENABLE
	MOV	AL,EOI			; END OF CURRENT INTERRUPT
	OUT	020H,AL 		;  SO FURTHER THINGS CAN HAPPEN
	PUSH	BP			; SAVE POINTER
	INT	5H			; ISSUE PRINT SCREEN INTERRUPT
	POP	BP			; RESTORE POINTER
	JMP	K27			; GO BACK WITHOUT EOI OCCURRING

K46:					; NOT-PRINT-SCREEN
	CMP	AL,59			; FUNCTION KEYS
	JB	K47			; NOT-UPPER-FUNCTION
	MOV	BX,OFFSET K12		; UPPER CASE PSEUDO SCAN CODES
	JMP	K63			; TRANSLATE_SCAN

K47:					; NOT-UPPER-FUNCTION
	MOV	BX,OFFSET K11		; POINT TO UPPER CASE TABLE
	JMP	SHORT K56		; OK, TRANSLATE THE CHAR

;------ KEYPAD KEYS, MUST TEST NUM LOCK FOR DETERMINATION

K48:					; KEYPAD-REGION
	TEST	KB_FLAG,NUM_STATE	; ARE WE IN NUM LOCK
	JNZ	K52			; TEST FOR SURE
	TEST	KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT	; ARE WE IN SHIFT STATE
	JNZ	K53			; IF SHIFTED, REALLY NUM STATE

;------ BASE CASE FOR KEYPAD

K49:					; BASE-CASE
					;
	CMP	AL,74			; SPECIAL CASE FOR A COUPLE OF KEYS
	JE	K50			; MINUS
	CMP	AL,78
	JE	K51
	SUB	AL,71			; CONVERT ORIGIN
	MOV	BX,OFFSET K15		; BASE CASE TABLE
	JMP	K64			; CONVERT TO PSEUDO SCAN

K50:	MOV	AX,74*256+'-'           ; MINUS
	JMP	SHORT K57		; BUFFER_FILL

K51:	MOV	AX,78*256+'+'           ; PLUS
	JMP	SHORT K57		; BUFFER_FILL

;------ MIGHT BE NUM LOCK, TEST SHIFT STATUS

K52:					; ALMOST-NUM-STATE
	TEST	KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT
	JNZ	K49			; SHIFTED TEMP OUT OF NUM STATE

K53:					; REALLY NUM STATE
	SUB	AL,70			; CONVERT ORIGIN
	MOV	BX,OFFSET K14		; NUM STATE TABLE
	JMP	SHORT K56		; TRANSLATE_CHAR

;------ PLAIN OLD LOWER CASE

K54:					; NOT-SHIFT
	CMP	AL,59			; TEST FOR FUNCTION KEYS
	JB	K55			; NOT-LOWER-FUNCTION
	MOV	AL,0			; SCAN CODE IN AH ALREADY
	JMP	SHORT K57		; BUFFER_FILL

K55:					; NOT-LOWER-FUNCTION
	MOV	BX,OFFSET K10		; LC TABLE

;------ TRANSLATE THE CHARACTER

K56:					; TRANSLATE-CHAR
	DEC	AL			; CONVERT ORIGIN
	XLAT	CS:K11			; CONVERT THE SCAN CODE TO ASCII

;------ PUT CHARACTER INTO BUFFER

K57:					; BUFFER_FILL
	CMP	AL,-1			; IS THIS AN IGNORE CHAR
	JE	K59			; YES, DO NOTHING WITH IT
	CMP	AH,-1			; LOOK FOR -1 PSEUDO SCAN
	JE	K59			; NEAR_INTERRUPT_RETURN

;------ HANDLE THE CAPS LOCK PROBLEM

K58:					; BUFFER_FILL-NOTEST
	TEST	KB_FLAG,CAPS_STATE	; ARE WE IN CAPS LOCK STATE
	JZ	K61			; SKIP IF NOT

;------ IN CAPS LOCK STATE

	TEST	KB_FLAG,LEFT_SHIFT+RIGHT_SHIFT	; TEST FOR SHIFT STATE
	JZ	K60			; IF NOT SHIFT, CONVERT LOWER TO UPPER

;------ CONVERT ANY UPPER CASE TO LOWER CASE

	CMP	AL,'A'                  ; FIND OUT IF ALPHABETIC
	JB	K61			; NOT-CAPS-STATE
	CMP	AL,'Z'
	JA	K61			; NOT_CAPS STATE
	ADD	AL,'a'-'A'              ; CONVERT TO LOWER CASE
	JMP	SHORT K61		; NOT_CAPS_STATE

K59:					; NEAR-INTERRUPT-RETURN
	JMP	K26			; INTERRUPT_RETURN

;------ CONVERT ANY LOWER CASE TO UPPER CASE

K60:					; LOWER-TO-UPPER
	CMP	AL,'a'                  ; FIND OUT IF ALPHABETIC
	JB	K61			; NOT_CAPS_STATE
	CMP	AL,'z'
	JA	K61			; NOT CAPS STATE
	SUB	AL,'a'-'A'              ; CONVERT TO UPPER CASE

K61:					; NOT-CAPS-STATE
	MOV	BX,BUFFER_TAIL		; GET THE END POINTER TO THE BUFFER
	MOV	SI,BX			; SAVE THE VALUE
	CALL	K4			; ADVANCE THE TAIL
	CMP	BX,BUFFER_HEAD		; HAS THE BUFFER WRAPPED AROUND
	JE	K62			; BUFFER_FULL_BEEP
	MOV	[SI],AX 		; STORE THE VALUE
	MOV	BUFFER_TAIL,BX		; MOVE THE POINTER UP
	CLI				; TURN OFF INTERRUPTS
	MOV	AL,EOI			; END OF INTERRUPT COMMAND
	OUT	020H,AL 		; SEND COMMAND TO INTERRUPT CONTROL PORT
	MOV	AL,ENA_KBD		; INSURE KEYBOARD IS ENABLED
	CALL	SHIP_IT 		; EXECUTE ENABLE
	MOV	AX,09102H		; MOVE IN POST CODE & TYPE
	INT	15H			; PERFORM OTHER FUNCTION
	JMP	K27A			; INTERRUPT_RETURN


;------ TRANSLATE SCAN FOR PSEUDO SCAN CODES

K63:					; TRANSLATE-SCAN
	SUB	AL,59			; CONVERT ORIGIN TO FUNCTION KEYS
K64:					; TRANSLATE-SCAN-ORGD
	XLAT	CS:K9			; CTL TABLE SCAN
	MOV	AH,AL			; PUT VALUE INTO AH
	MOV	AL,0			; ZERO ASCII CODE
	JMP	K57			; PUT IT INTO THE BUFFER

KB_INT_1	ENDP

K62:
	MOV	AL,EOI		; ENABLE INTR. CTL. CHIP
	OUT	INTA00,AL	;
	MOV	BX,82H		; NUMBER OF CYCLES FOR 1/8 SECOND TONE
	IN	AL,KB_CTL	; GET CONTROL INFORMATION
	PUSH	AX		; SAVE
K65:				; BEEP-CYCLE
	AND	AL,0FCH 	; TURN OFF TIMER GATE AND SPEAKER DATA
	JMP	SHORT $+2	; IO DELAY
	OUT	KB_CTL,AL	; OUTPUT TO CONTROL
	MOV	CX,0CEH 	; HALF CYCLE TIME FOR TONE
K66:	LOOP	K66		; SPEAKER OFF
	OR	AL,2		; TURN ON SPEAKER BIT
	OUT	KB_CTL,AL	; OUTPUT TO CONTROL
	MOV	CX,0E5H 	; SET UP COUNT
K67:	LOOP	K67		; ANOTHER HALF CYCLE
	DEC	BX		; TOTAL TIME COUNT
	JNZ	K65		; DO ANOTHER CYCLE
	POP	AX		; RECOVER CONTROL
	OUT	KB_CTL,AL	; OUTPUT THE CONTROL
	JMP	K27		; EXIT

;------------------------------------------------------------------------------
;
;	SND_DATA
;
;		THIS ROUTINES HANDLES TRANSMISSION OF COMMAND AND DATA BYTES
;		TO THE KEYBOARD AND RECEIPT OF ACKNOWLEDGEMENTS.  IT ALSO
;		HANDLES ANY RETRIES IF REQUIRED
;
;------------------------------------------------------------------------------

SND_DATA PROC	NEAR
	PUSH	AX		; SAVE REGISTERS
	PUSH	BX		; "
	PUSH	CX
	MOV	BH,AL		; SAVE TRANSMITTED BYTE FOR RETRIES
	MOV	BL,3		; LOAD RETRY COUNT
SD0:	CLI			; DISABLE INTERRUPTS
	AND	KB_FLAG_2,NOT (KB_FE+KB_FA) ; CLEAR ACK AND RESEND FLAGS

;------- WAIT FOR COMMAND TO ACCEPTED

	SUB	CX,CX		;
SD5:
	IN	AL,STATUS_PORT	;
	TEST	AL,INPT_BUF_FULL
	LOOPNZ	SD5		; WAIT FOR COMMAND TO BE ACCEPTED
;
	MOV	AL,BH		; REESTABLISH BYTE TO TRANSMIT
	OUT	PORT_A,AL	; SEND BYTE
	STI			; ENABLE INTERRUPTS
	MOV	CX,01A00H	; LOAD COUNT FOR 10 ms+
SD1:	TEST	KB_FLAG_2,KB_FE+KB_FA ; SEE IF EITHER BIT SET
	JNZ	SD3		; IF SET, SOMETHING RECEIVED GO PROCESS
;
	LOOP	SD1		; OTHERWISE WAIT
;
SD2:	DEC	BL		; DECREMENT RETRY COUNT
	JNZ	SD0		; RETRY TRANSMISSION
;
	OR	KB_FLAG_2,KB_ERR ; TURN ON TRANSMIT ERROR FLAG
	JMP	SHORT SD4	; RETRIES EXHAUSTED FORGET TRANSMISSION
;
SD3:	TEST	KB_FLAG_2,KB_FA ; SEE IF THIS IS AN ACKNOWLEDGE
	JZ	SD2		; IF NOT, GO RESEND
;
SD4:
	POP	CX		; RESTORE REGISTERS
	POP	BX		;
	POP	AX		; *
	RET			; RETURN, GOOD TRANSMISSION
SND_DATA ENDP

;------------------------------------------------------------------------------
;	SND_LED
;
;		THIS ROUTINES TURNS ON THE MODE INDICATORS.
;
;------------------------------------------------------------------------------
SND_LED PROC	NEAR
	CLI			; TURN OFF INTERRUPTS
	TEST	KB_FLAG_2,KB_PR_LED ; CHECK FOR MODE INDICATOR UPDATE
	JNZ	SL1		; DONT UPDATE AGAIN IF UPDATE UNDERWAY
;
	OR	KB_FLAG_2,KB_PR_LED ; TURN ON UPDATE IN PROCESS
	MOV	AL,EOI		; END OF INTERRUPT COMMAND
	OUT	020H,AL 	; SEND COMMAND TO INTERRUPT CONTROL PORT
	JMP	SHORT SL0	; GO SEND MODE INDICATOR COMMAND
;
SND_LED1:
	CLI			; TURN OFF INTERRUPTS
	TEST	KB_FLAG_2,KB_PR_LED ; CHECK FOR MODE INDICATOR UPDATE
	JNZ	SL1		; DONT UPDATE AGAIN IF UPDATE UNDERWAY
;
	OR	KB_FLAG_2,KB_PR_LED ; TURN ON UPDATE IN PROCESS
SL0:	MOV	AL,LED_CMD	; LED CMD BYTE
	CALL	SND_DATA	; SEND DATA TO KEYBOARD
	CLI			;
	CALL	MAKE_LED	; GO FORM INDICATOR DATA BYTE
	AND	KB_FLAG_2,0F8H	; CLEAR MODE INDICATOR BITS
	OR	KB_FLAG_2,AL	; SAVE PRESENT INDICATORS STATES FOR NEXT TIME
	TEST	KB_FLAG_2,KB_ERR ; TRANSMIT ERROR DETECTED
	JNZ	SL2		; IF YES, BYPASS SECOND BYTE TRANSMISSION
;
	CALL	SND_DATA	; SEND DATA TO KEYBOARD
	CLI			; TURN OFF INTERRUPTS
	TEST	KB_FLAG_2,KB_ERR ; TRANSMIT ERROR DETECTED
	JZ	SL3		; IF NOT, DONT SEND AN ENABLE COMMAND
;
SL2:	MOV	AL,KB_ENABLE	; GET KEYBOARD CSA ENABLE COMMAND
	CALL	SND_DATA	; SEND DATA TO KEYBOARD
	CLI			; TURN OFF INTERRUPTS
SL3:	AND	KB_FLAG_2,NOT(KB_PR_LED+KB_ERR) ; TURN OFF MODE INDICATOR
				; UPDATE AND TRANSMIT ERROR FLAG
SL1:	STI			; ENABLE INTERRUPTS
	RET			; RETURN TO CALLER
SND_LED ENDP

;------------------------------------------------------------------------------
;	MAKE_LED
;
;		THIS ROUTINES FORMS THE DATA BYTE NECESSARY TO TURN ON/OFF
;		THE MODE INDICATORS
;
;------------------------------------------------------------------------------
MAKE_LED PROC	NEAR
	PUSH	CX		; SAVE CX
	MOV	AL,KB_FLAG	; GET CAPS & NUM LOCK INDICATORS
	AND	AL,CAPS_STATE+NUM_STATE+SCROLL_STATE ; ISOLATE INDICATORS
	MOV	CL,4		; SHIFT COUNT
	ROL	AL,CL		; SHIFT BITS OVER TO TURN ON INDICATORS
	AND	AL,07H		; MAKE SURE ONLY MODE BITS ON
	POP	CX		;
	RET			; RETURN TO CALLER
MAKE_LED ENDP
;------------------------------------------------------------------------------
;	SHIP_IT
;
;		THIS ROUTINES HANDLES TRANSMISSION OF COMMAND AND DATA BYTES
;		TO THE KEYBOARD CONTROLLER.
;
;------------------------------------------------------------------------------
SHIP_IT PROC	NEAR
	PUSH	AX		; SAVE DATA TO SEND
;------- WAIT FOR COMMAND TO ACCEPTED

	CLI			; DISABLE INTERRUPTS
	SUB	CX,CX		; CLEAR COUNTER
S10:
	IN	AL,STATUS_PORT	;
	TEST	AL,INPT_BUF_FULL
	LOOPNZ	S10		; WAIT FOR COMMAND TO BE ACCEPTED
;
	POP	AX		; GET DATA TO SEND
	OUT	STATUS_PORT,AL	; SEND TO KEYBOARD CONTROLLER
	STI			; ENABLE INTERRUPTS AGAIN
	RET			; RETURN TO CALLER
SHIP_IT ENDP
CODE	ENDS
	END
